﻿#region License
/*
Copyright (c) 2009, Open University of the Netherlands
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided 
that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the 
  following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
  the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of Open University of the Netherlands nor the names of its contributors may be 
  used to endorse or promote products derived from this software without specific prior written 
  permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
*/
#endregion

namespace EwbDesigner.NET
{
    using System;
    using XmlTreeNode = System.Collections.Generic.SimpleTreeNode<EwbDesigner.NET.XmlCacheItem>;
    using XsdTreeNode = System.Collections.Generic.SimpleTreeNode<EwbDesigner.NET.XsdCacheItem>;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Diagnostics;
    using System.Globalization;
    using System.Windows.Forms.Design;

    using EwbDesigner.NET.Classes;

    using WeifenLuo.WinFormsUI.Docking;
    using EwbDesigner.NET.Editors;

    public partial class PropertiesForm : DockContent
    {
        #region Fields

        static readonly PropertiesForm _instance = new PropertiesForm();

        private static Char[] ListSeparator = CultureInfo.CurrentCulture.TextInfo.ListSeparator.ToCharArray();

        private PropertyTable propTable = new PropertyTable();

        #endregion Fields

        #region Constructors

        private PropertiesForm()
        {
            InitializeComponent();

            propertyGrid1.SelectedObject = propTable;

            HideOnClose = true;
        }

        #endregion Constructors

        #region Properties

        //Visible when reflecting
        public static PropertiesForm Instance
        {
            get { return _instance; }
        }

        #endregion Properties

        #region Event Handlers

        private void PropertiesForm_DockStateChanged(object sender, System.EventArgs e)
        {
            if (DockState == DockState.Unknown)
            {
                Blank();
            }
        }

        private void propertyGrid1_PropertyValueChanged(object s, System.Windows.Forms.PropertyValueChangedEventArgs e)
        {
            if (Data.editing != null)
            {
                //Debug.WriteLine(s.GetType().Name, "Trace");
                //Debug.WriteLine(e.ChangedItem.Label, "Trace");

                XsdTreeNode xsd = Data.XsdModel.NodeByPath(Data.editing.Path);
                if (xsd != null && xsd.Value.attributes[e.ChangedItem.Label].IsFlagSet(XsdAttributeFlags.TrimBlanks))
                {
                    Data.editing.Value.attributes[e.ChangedItem.Label].value = ((String)e.ChangedItem.Value).Trim();
                }
                else
                {
                    Data.editing.Value.attributes[e.ChangedItem.Label].value = e.ChangedItem.Value.ToString();
                }

                StructureForm.Structure.RefreshObject(StructureForm.Structure.SelectedObject);
            }
        }

        private void propertyGrid1_SelectedGridItemChanged(object sender, System.Windows.Forms.SelectedGridItemChangedEventArgs e)
        {
            if (e.OldSelection != null)
            {
                //Debug.WriteLine(e.OldSelection.Label);
            }
        }

        #endregion Event Handlers

        #region Methods

        public void CreateEditor(XmlTreeNode node)
        {
            propTable.Properties.Clear();

            Data.editing = node;

            String TagPath = Data.editing.Path;
            String TagText = Data.editing.Value.name;

            //Construct PropertyTable entries from XSD attributes.
            PropertySpec ps = new PropertySpec(TagPath, "System.String");

            //NOTE FIND XSD NODE BELONGING TO XML NODE
            XsdTreeNode xsd = Data.XsdModel.NodeByPath(node.Path);
            if (xsd == null)
            {
                Debug.WriteLine("Error Retrieving XSD by XML key");
            }

            //NOTE BEWARE MOST OF THE FOLOWING CODE SHOULD WORK ON XsdCacheItem.Attributes and NOT Xml
            foreach (String key in xsd.Value.attributes.Keys)
            {
                #region Create Base PropertySpec

                //Use XsdCacheItem Stuff First to define the editor.
                //Only take the actual value from the XmlCacheItem.
                String ExportedAs = "Fallback (ExportedAs_" + xsd.Value.name + ")";
                if (xsd.Value.attributes.ContainsKey("ExportedAs"))
                {
                    ExportedAs = xsd.Value.attributes["ExportedAs"].value;
                }

                //Get Editor.
                XsdAttributeEditor editor = Data.editing.Value.attributes[key].editor;

                //Get Annotation if any or generate a replacement.
                String Annotation = "";
                if (String.IsNullOrEmpty(xsd.Value.attributes[key].annotation))
                {
                    Annotation = " Attribute of: " + TagText + "\r\n" +                  //Automatic Description...
                                 " Exported as: " + ExportedAs + "\r\n" +                //Automatic Description...
                        //       " DataType: " + xsd.Value.attributes[key].type + " (" + xsd.Value.attributes[key].flags.ToString() + ")\r\n" +
                                 " Path: " + TagPath;
                }
                else
                {
                    Annotation = xsd.Value.attributes[key].annotation;
                }

                String Category = (key == "ExportedAs") ? "{export}" : TagText;

                switch (editor)
                {
                    case XsdAttributeEditor.ArrayofString:
                        {
                            ps = new PropertySpec(
                                xsd.Value.attributes[key].name,                         //Property Name
                                xsd.Value.attributes[key].type,                         //Property Editor Type
                                Category,                                                //Category...
                                Annotation,                                             //Automatic Description...
                                new StringCollection()                                  //Default Value is set below...
                                );

                            //Set Default Value
                            ((StringCollection)ps.DefaultValue).AddRange(Data.editing.Value.attributes[key].defaultvalue.Split(ListSeparator));
                            break;
                        }
                    default:
                        {
                            ps = new PropertySpec(
                                xsd.Value.attributes[key].name,                         //Property Name
                                xsd.Value.attributes[key].type,                         //Property Editor Type
                                Category,                                                //Category...
                                Annotation,                                             //Automatic Description...
                                Data.editing.Value.attributes[key].defaultvalue         //Default Value, Actual Value set below...
                                );
                            break;
                        }
                }

                #endregion Create Base PropertySpec

                #region Adjust PropertySpec ConverterType

                if (xsd.Value.attributes[key].IsFlagSet(XsdAttributeFlags.NoWhiteSpace))
                {
                    ps.ConverterTypeName = typeof(WhiteSpaceConverter).AssemblyQualifiedName;
                }

                #endregion Adjust PropertySpec ConverterType

                #region Adjust PropertySpec EditorTypeName

                //These flags are mutually exclusive.
                switch (xsd.Value.attributes[key].editor)
                {
                    case XsdAttributeEditor.MultiLine:
                        {
                            ps.EditorTypeName = typeof(MultilineStringEditor).AssemblyQualifiedName;
                            break;
                        }
                    case XsdAttributeEditor.Filename:
                        {
                            ps.EditorTypeName = typeof(FileNameEditor).AssemblyQualifiedName;
                            break;
                        }
                    case XsdAttributeEditor.ArrayofString:
                        {
                            ps.EditorTypeName = typeof(CustomStringCollectionEditor).AssemblyQualifiedName;
                            break;
                        }
                    case XsdAttributeEditor.Enumeration:
                        {
                            ps.EditorTypeName = typeof(CustomXsdCacheItemEditor).AssemblyQualifiedName;
                            break;
                        }
                    case XsdAttributeEditor.Html:
                        {
                            ps.EditorTypeName = typeof(CustomHtmlEditor).AssemblyQualifiedName;
                            break;
                        }
                }

                #endregion Adjust PropertySpec EditorTypeName

                #region Adjust PropertySpec ReadOnly

                //Support Read-Only (Fixed Valued) Attributes...
                if (xsd.Value.attributes[key].IsFlagSet(XsdAttributeFlags.FixedValue))
                {
                    ps.Attributes = new Attribute[]
                            {
                                ReadOnlyAttribute.Yes
                            };
                }

                #endregion Adjust PropertySpec ReadOnly

                //Add the Property to the PropertyTable.
                propTable.Properties.Add(ps);

                #region Ajust Value

                //Set the actual value of the Property.
                //This will enable the bold title display when its value is changed.
                if (Data.editing.Value.attributes[key].value != null)
                {
                    switch (xsd.Value.attributes[key].editor)
                    {
                        case XsdAttributeEditor.Enumeration:
                        case XsdAttributeEditor.ArrayofString:
                        case XsdAttributeEditor.Html:
                            {
                                //Take the whole XsdCacheItem as value for custom editors.
                                propTable[key] = Data.editing.Value.attributes[key];
                                break;

                            }

                        //  {
                        //      propTable[key] = new StringCollection();

                        //      //Convert String to StringCollection...
                        //      String[] sa = Data.editing.Value.attributes[key].value.Split(ListSeparator);

                        //      foreach (String s in sa)
                        //      {
                        //          ((StringCollection)propTable[key]).Add(s);
                        //      }
                        //  }

                        default:
                            {
                                //Take the XsdCacheItem.value as value for default editors.
                                propTable[key] = Data.editing.Value.attributes[key].value;
                                break;
                            }
                    }
                }

                #endregion Ajust Value

            }

            if (xsd == null || String.IsNullOrEmpty(xsd.Value.annotation))
            {
                HelpForm.Instance.Content = String.Format(@"<HTML><TITLE>{0}</TITLE><BODY><B>{0}</B><HR />{1}</BODY></HTML>", TagText, TagPath);
            }
            else
            {
                HelpForm.Instance.Content = String.Format(@"<HTML><TITLE>{0}</TITLE><BODY><B>{0}</B><HR />{1}</BODY></HTML>", TagText, xsd.Value.annotation);
            }

            PropertiesForm.Instance.propertyGrid1.SelectedObject = propTable;
        }

        public void RemoveEditor()
        {
            Data.editing = null;

            PropertiesForm.Instance.propertyGrid1.SelectedObject = null;
            PropertiesForm.Instance.propertyGrid1.Refresh();

            propTable.Properties.Clear();

            PropertiesForm.Instance.propertyGrid1.SelectedObject = propTable;
        }

        private void Blank()
        {
            //if (!propertyGrid1.IsDisposed)
            //{
            //    propertyGrid1.SelectedObject = null;
            //}
        }

        #endregion Methods

        #region Nested Types

        /// <summary>
        /// Removes Whitespace from String Properties.
        /// </summary>
        public class WhiteSpaceConverter : TypeConverter
        {
            #region Methods

            // Overrides the CanConvertFrom method of TypeConverter.
            // The ITypeDescriptorContext interface provides the context for the
            // conversion. Typically, this interface is used at design time to
            // provide information about the design-time container.
            public override bool CanConvertFrom(ITypeDescriptorContext context,
                Type sourceType)
            {
                if (sourceType == typeof(String))
                {
                    return true;
                }
                return base.CanConvertFrom(context, sourceType);
            }

            // Overrides the ConvertFrom method of TypeConverter.
            public override object ConvertFrom(ITypeDescriptorContext context,
                CultureInfo culture, object value)
            {
                if (value is String)
                {
                    //    string[] v = ((string)value).Split(new char[] { ',' });
                    //    return new Point(int.Parse(v[0]), int.Parse(v[1]));
                    return value.ToString().Replace(' ', '_');
                }
                return base.ConvertFrom(context, culture, value);
            }

            // Overrides the ConvertTo method of TypeConverter.
            public override object ConvertTo(ITypeDescriptorContext context,
                CultureInfo culture, object value, Type destinationType)
            {
                if (destinationType == typeof(String))
                {
                    //return ((Point)value).X + "," + ((Point)value).Y;
                    return value.ToString().Replace(' ', '_');
                }
                return base.ConvertTo(context, culture, value, destinationType);
            }

            #endregion Methods
        }

        #endregion Nested Types
    }
}